using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading;
using MalwareMultiScan.Backends.Services.Interfaces;
using Microsoft.Extensions.Logging;

namespace MalwareMultiScan.Backends.Services.Implementations
{
    /// <summary>
    /// 
    /// </summary>
    [ExcludeFromCodeCoverage]
    public class ProcessRunner : IProcessRunner
    {
        private readonly ILogger<ProcessRunner> _logger;

        /// <summary>
        /// Initialize process runner.
        /// </summary>
        /// <param name="logger">Logger.</param>
        public ProcessRunner(ILogger<ProcessRunner> logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="arguments"></param>
        /// <param name="cancellationToken"></param>
        /// <param name="standardOutput"></param>
        /// <param name="standardError"></param>
        /// <returns></returns>
        /// <exception cref="TimeoutException"></exception>
        public int RunTillCompletion(string path, string arguments, CancellationToken cancellationToken,
            out string standardOutput, out string standardError)
        {
            var process = new Process
            {
                StartInfo = new ProcessStartInfo(path, arguments)
                {
                    RedirectStandardError = true,
                    RedirectStandardOutput = true,
                    WorkingDirectory = Path.GetDirectoryName(path) ?? Directory.GetCurrentDirectory()
                }
            };

            _logger.LogInformation(
                $"Starting process {process.StartInfo.FileName} " +
                $"with arguments {process.StartInfo.Arguments} " +
                $"in working directory {process.StartInfo.WorkingDirectory}");

            process.Start();

            cancellationToken.Register(() =>
            {
                if (process.HasExited)
                    return;

                process.Kill(true);

                throw new TimeoutException("Scanning failed to complete within the timeout");
            });

            process.WaitForExit();

            _logger.LogInformation($"Process has exited with code {process.ExitCode}");

            standardOutput = process.StandardOutput.ReadToEnd();
            standardError = process.StandardError.ReadToEnd();

            _logger.LogDebug($"Process standard output: {standardOutput}");
            _logger.LogDebug($"Process standard error: {standardError}");

            return process.ExitCode;
        }
    }
}